import {ActionTree,Store,ActionContext,Payload, MutationTree} from 'vuex'
import LRU from 'lru-cache'
import { Instantiation } from '@/utils';
import LRUCache from 'lru-cache';
import { ElMessage } from 'element-plus';
export abstract class ActionBase<RT,S=RT,P=any,R=any> extends Instantiation implements Payload {

    public abstract  handle(store:Store<RT>,ctx:ActionContext<S,RT>,payload:P):Promise<R>;

    get type() {
        return this.getActionType();
    }

    constructor(public payload?:P) {
        super();
    }

    public getActionType() {
        return this.constructor.name;
    }

    public static register(actionTree:ActionTree<any,any>) {
        const ctr:any = this;
        actionTree[this.name] = async function(ctx,payload){
            try {
                if(typeof payload?.handle === 'function') {
                    return await Promise.resolve(payload.handle(this,ctx,payload.payload));
                }else {
                    return await Promise.resolve(new ctr().handle(this,ctx,payload));
                }
            }catch(e) {
                ElMessage.error({
                    type:"error",
                    message:`action 执行错误: ${e}`
                })
                throw e;
            }

        }
    }

    public call(store:Store<RT>,payload:P):Promise<R> {
        this.payload = payload;
        return store.dispatch(this)
    }
}

export abstract class ActionCache<RT,S=RT,P=any,R=any> extends ActionBase<RT,S,P,R> {
    static cacheAge = 2000;
    static cacheMax = 100;
    cachePool:LRUCache<any, any>
    constructor(payload?:P) {
        super(payload);
        const cachePool = new LRU({
            max:(this.constructor as any).cacheMax ?? 100,
            maxAge:(this.constructor as any).cacheAge ?? 2000
        })
        this.cachePool = cachePool;
        const oldHandle = this.handle;
        this.handle = async function(store:Store<RT>,ctx:ActionContext<S,RT>,payload:P):Promise<R> {
            const key = this.hash(store,ctx,payload);
            if(cachePool.has(key)){
                return cachePool.get(key) as any
            }
            const res = await oldHandle(store,ctx,payload);
            cachePool.set(key,res);
            return res;
        }
    }
    hash(store:Store<RT>,ctx:ActionContext<S,RT>,payload:P):string {
        return JSON.stringify(payload);
    }
    resetCache(store:Store<RT>,ctx:ActionContext<S, RT>,payload:P) {
        const hashKey = this.hash(store,ctx,payload)
        this.cachePool.del(hashKey)
    }
    resetAllCache() {
        this.cachePool.reset()
    }
}
export abstract class MutationBase<RT,P=any> extends Instantiation  implements Payload {

    public abstract mutation(store:RT,payload:P):void;

    constructor(public payload?:P) {
        super();
    }
    get type() {
        return this.getMutationName();
    }
    public getMutationName() {
        return this.constructor.name;
    }
    public static getMutationName() {
        return this.name;
    }
    public static register(commitTree:MutationTree<any>) {
        const ctr:any = this;
        commitTree[this.getMutationName()] = function(state: any, payload?: any) {
            if(typeof payload?.mutation === 'function') {
                return payload.mutation(state,payload.payload)
            } else {
                return new ctr().mutation(state,payload);
            }
        }
    }

    public call(store:Store<RT>,payload:P):void {
        this.payload = payload;
        store.commit(this);

    }

}


export function registerAll<T extends Record<string,any>>(target:T,...r:{register(tree:T):any}[]) {
    for(const item of r) {
        item.register(target);
    }
}
